﻿/****************************************************************
  Author:  Ievgenii Nazaruk, ievgenii.nazaruk@gmail.com 
 License:  Microsoft Public License (Ms-PL)
          ( http://www.opensource.org/licenses/ms-pl.html or
            http://www.microsoft.com/opensource/licenses.mspx )
*****************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HtmlAgilityPack;

namespace WebPageParser
{
    /// <summary>
    /// This exception is thrown when table was expected
    /// at least N columns, but the actual number of 
    /// columns in the table is less.
    /// </summary>
    class NotEnoughColumnsExceptions : Exception
    {
    }

    /// <summary>
    /// Represents a row in html table.
    /// </summary>
    class HtmlTableRow: List<String>
    {
    }

    /// <summary>
    /// Parsers each row of web page using specified transformer function
    /// which transfroms html-representation into an object
    /// of specific type (specific row type).
    /// </summary>
    class HtmlTableParser
    {
        private int ColumnCount = 0;
        private HtmlNode TableNode = null;

        public HtmlTableParser(int columnCount, HtmlNode tableNode)
        {
            this.ColumnCount = columnCount;
            this.TableNode = tableNode;
        }

        private HtmlTableRow m_header = null;
        public  HtmlTableRow Header
        {
            get
            {
                if (m_header == null)
                {
                    Parse();
                }
                return m_header;
            }
        }

        private HtmlTableRow m_htmlHeader = null;
        public HtmlTableRow HtmlHeader
        {
            get
            {
                if (m_htmlHeader == null)
                {
                    Parse();
                }
                return m_htmlHeader;
            }
        }

        private List<HtmlTableRow> m_content = null;
        public  List<HtmlTableRow> Content
        {
            get
            {
                if (m_content == null)
                {
                    Parse();
                }
                return m_content;
            }
        }

        private List<HtmlTableRow> m_htmlContent = null;
        public List<HtmlTableRow> HtmlContent
        {
            get
            {
                if (m_htmlContent == null)
                {
                    Parse();
                }
                return m_htmlContent;
            }
        }

        protected void Parse()
        {
            m_header = new HtmlTableRow();
            m_htmlHeader = new HtmlTableRow();
            m_content = new List<HtmlTableRow>();
            m_htmlContent = new List<HtmlTableRow>();


            HtmlNode row = TableNode.FirstChild;
            ParseHeader(row);
            
            row = row.NextSibling;
            ParseContent(row);

        }

        void ParseHeader(HtmlNode headerRowNode)
        {
            /* header */
            HtmlNode column = headerRowNode.FirstChild;

            /* ignore first column in the header */
            column = column.NextSibling;

            /* fill out header*/
            for (int i = 0; i < ColumnCount; i++)
            {
                if (column == null)
                {
                    throw new NotEnoughColumnsExceptions();
                }

                m_header.Add(column.InnerText);
                m_htmlHeader.Add(column.InnerHtml);
                column = column.NextSibling;
            }           
        }

        void ParseContent(HtmlNode firstContentRow)
        {
            HtmlNode contentRow = firstContentRow;

            while (contentRow != null)
            {
                if (contentRow.ChildNodes.Count != ColumnCount)
                {
                    contentRow = contentRow.NextSibling;
                    continue;
                }

                HtmlTableRow newTextContentRow = new HtmlTableRow();
                HtmlTableRow newHtmlContentRow = new HtmlTableRow();
                HtmlNode column = contentRow.FirstChild;

                for (int i = 0; i < ColumnCount; i++)
                {
                    if(column == null)
                    {
                        throw new NotEnoughColumnsExceptions();
                    }

                    newTextContentRow.Add(column.InnerText);
                    newHtmlContentRow.Add(column.InnerHtml);
                    column = column.NextSibling;
                }

                Content.Add(newTextContentRow);
                HtmlContent.Add(newHtmlContentRow);
                contentRow = contentRow.NextSibling;
            }
        }


        public delegate RowType TransformRow<RowType>(HtmlTableRow textRow, HtmlTableRow htmlRow);
        public void TransformTable<RowType>(List<RowType> content, TransformRow<RowType> transformer)
        {
            for (int i = 0; i < Content.Count || i < HtmlContent.Count;i++ )
            {
                if (i < Content.Count && i < HtmlContent.Count)
                {
                    content.Add(transformer(Content[i], HtmlContent[i]));
                }
                else if (i < Content.Count)
                {
                    content.Add(transformer(Content[i], null));
                }
                else
                {
                    content.Add(transformer(null, HtmlContent[i]));
                }
            }
        }

    }
}
